#include "ftadc.h"
#include "DrvUART010.h"
#include "unione_lite.h"
#include "interrupt.h"
#include <string.h>

#define SET_VAL_BITS(x, bit_num, off, val)   x &= ~(((1 << bit_num)-1) << off); x |= (val << off)

adc_callback g_adc_cb[CHANNEL_NUMS] = {NULL};

static int s_pre_adc_value[CHANNEL_NUMS] = {0};
static int s_adc_count[CHANNEL_NUMS] = {0};
volatile int g_debounce_count[CHANNEL_NUMS] = {0};
volatile int g_debounce_gap[CHANNEL_NUMS] = {0};

static u8 seq2[] = {2,3,0,1,4,5,6,7};//first scan 2 channel, then scan 3 channel,....

static u32 _uni_ack_key(u8 ch_num, u32 status)
{
	u32 value;
	value = ftadc_get_data(ch_num);
	if(value > s_pre_adc_value[ch_num]){
		if((value - s_pre_adc_value[ch_num]) > g_debounce_gap[ch_num]) {
			s_pre_adc_value[ch_num] = value;
			s_adc_count[ch_num] = 0;
			return 0;
		}else{
			s_adc_count[ch_num]++;
		}
	}else{
		if((s_pre_adc_value[ch_num] - value) > g_debounce_gap[ch_num]) {
			s_pre_adc_value[ch_num] = value;
			s_adc_count[ch_num] = 0;
			return 0;
		}else{
			s_adc_count[ch_num]++;
		}
	}
	s_pre_adc_value[ch_num] = value;

	if(s_adc_count[ch_num] >= g_debounce_count[ch_num]){
		s_adc_count[ch_num] = 0;
		if (g_adc_cb[ch_num]) {
			g_adc_cb[ch_num](value, (status >> 16));
		}
	}

	return 0;
}

static void adc_irq(void){
	u32 status;
	int i = 0;
	status = read_mreg32(ADC_FTADCC010_PA_BASE + ADC_INTR);
	if (status & 0x1) {
		for (i = 0; i < 8; ++i) {
			if ((status >> (8 + i)) & 0x1) {
				_uni_ack_key(i, status);
			}
		}
		write_mreg32(ADC_FTADCC010_PA_BASE + ADC_INTR, status);
	}
}

/*************************************************************************
for example:
    seq[] = {0,1,2,3,4,5,6,7}; the scan seq is adc0 channel---adc7 channel
    the array size is fix 8
*************************************************************************/
static void _uni_adc_seq_conf(u8 seq[])
{
	u32 tmp = 0;
	
	tmp = (seq[7]<<28) | (seq[6]<<24) | (seq[5]<<20) | (seq[4]<<16) | (seq[3]<<12) |
		(seq[2]<<8) | (seq[1]<<4) | seq[0];

	write_mreg32(ADC_FTADCC010_PA_BASE + ADC_SQR, tmp);
}

/*********************************************************************************************
1. en: 0:disable adc 1:enable
2. swstart: 0:software trigger stop 1: software trigger start
3. scan_num: the range is 0-7, 0 means just scanning one channel,7 means scanning 8 channels
4. mode: SINGLE_STEP,SINGLE_SCAN,CONTINUE_SCAN
**********************************************************************************************/
static void _uni_adc_ctl(u8 en, u8 swstart, enum ADC_MODE mode, u8 scan_num)
{
	u32 tmp;
	tmp = read_mreg32(ADC_FTADCC010_PA_BASE + ADC_CTRL);
	SET_VAL_BITS(tmp, 1, 0, en);
	SET_VAL_BITS(tmp, 1, 4, swstart);
	SET_VAL_BITS(tmp, 3, 16, scan_num);
	SET_VAL_BITS(tmp, 1, 15, 0);
	SET_VAL_BITS(tmp, 1, 13, 0);
	SET_VAL_BITS(tmp, 2, 8, mode);
	write_mreg32(ADC_FTADCC010_PA_BASE + ADC_CTRL, tmp);
}

void ftadc_enable_irq(void)
{
	write_mreg32(ADC_FTADCC010_PA_BASE + ADC_INTEN, 0xffff0000); //enable all thre irq
	fLib_EnableInt(FTADC_IRQ);
}

void ftadc_disable_irq(void)
{
	write_mreg32(ADC_FTADCC010_PA_BASE + ADC_INTEN, 0x0);
	fLib_DisableInt(FTADC_IRQ);
}

/*********************************************************************************************
channel: just support channel-2 and channel-3
**********************************************************************************************/
u32 ftadc_get_data(u8 ch_num)
{
	u32 tmp = 0;
	tmp = read_mreg32(ADC_FTADCC010_PA_BASE + ADC_DATA0 + ch_num * 4);
	return (tmp & 0xfff);
}

/*********************************************************************************************
channel: just support channel-2 and channel-3
thre_event_num: range 0~15
is_high_thre: 1:high, 0:low
thre_value:
  when is_high_thre = 1:over thre_value will trigger interrupt, 0xffff will disable the function
  when is_high_thre = 0:below threshold threshold_l trigger interrupt, 0x0000 will disable the function
**********************************************************************************************/
void ftadc_set_threshold(u8 channel, int thre_event_num, u8 is_high_thre, int thre_value)
{
	u32 tmp = 0;
	int off = (thre_event_num % 2) * 16;
	int reg_off = thre_event_num / 2 * 4;
	tmp = read_mreg32(ADC_FTADCC010_PA_BASE + ADC_THRHOLD0 + reg_off);
	SET_VAL_BITS(tmp, 1, (15 + off), is_high_thre);
	SET_VAL_BITS(tmp, 3, (12 + off), channel);
	SET_VAL_BITS(tmp, 12, (0 + off), thre_value);
	write_mreg32(ADC_FTADCC010_PA_BASE + ADC_THRHOLD0 + reg_off, tmp);
}

void ftadc_adc_start(){
	_uni_adc_ctl(1,1,CONTINUE_SCAN,2);
}

void ftadc_adc_stop(){
	_uni_adc_ctl(0,0,CONTINUE_SCAN,0);
}

/*********************************************************************************************
channel: just support channel-2 and channel-3
threshold_h: over threshold_h will trigger interrupt, 0xffff will disable the function
threshold_l: below threshold threshold_l trigger interrupt, 0x0000 will disable the function
**********************************************************************************************/
int ftadc_init(void)
{
	u32 tmp;

	// release rst_n
	tmp = read_mreg32(SYSC_PA_BASE+0x81c);
	tmp = tmp | (1 << 4);
	write_mreg32(SYSC_PA_BASE+0x81c , tmp);

	_uni_adc_seq_conf(seq2);
	return 0;

}

int ftadc_init_irq(void)
{
	int i;

	for (i = 0; i < CHANNEL_NUMS; ++i) {
		g_adc_cb[i] = NULL;
		s_pre_adc_value[i] = 0;
		s_adc_count[i] = 0;
		g_debounce_count[i] = 10;
		g_debounce_gap[i] = 10;
	}

	if(fLib_ConnectInt(FTADC_IRQ, (PrVoid)adc_irq) == 0)
	{
		fLib_printf("Fail to register adc_irq \r\n");
		return -1;
	}
	return 0;
}

int ftadc_final(void)
{
	ftadc_disable_irq();
	return 0;
}

